package com.glsc.ngateway.platform.utils;

import com.google.common.base.Strings;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

import javax.persistence.criteria.*;
import java.util.Collection;
import java.util.List;

/**
 * 功能描述: <br>
 *
 * @author gaojc
 * @date 2020/9/3 17:02
 */
public class SpecificationFactory {
    public static Specification build(OP op, SpecElement... sarray) {
        return (root, query, builder) -> {
            Predicate p_ = null;
            for (SpecElement s : sarray) {
                Type oper = s.getOper();
                String name = s.getName();
                Object value = s.getValue();
                if (value == null && oper != Type.ISNULL && oper != Type.ISNOTNULL) {
                    continue;
                }

                Predicate p;
                switch (oper) {
                    case EQ: {
                        p = builder.equal(buildEx(root, name), value);
                        break;
                    }
                    case NEQ: {
                        p = builder.notEqual(buildEx(root, name), value);
                        break;
                    }
                    case GT: {
                        p = builder.greaterThan(buildEx(root, name), (Comparable) value);
                        break;
                    }
                    case GE: {
                        p = builder.greaterThanOrEqualTo(buildEx(root, name), (Comparable) value);
                        break;
                    }
                    case LT: {
                        p = builder.lessThan(buildEx(root, name), (Comparable) value);
                        break;
                    }
                    case LE: {
                        p = builder.lessThanOrEqualTo(buildEx(root, name), (Comparable) value);
                        break;
                    }
                    case LIKE: {
                        p = builder.like(buildEx(root, name), "%" + value + "%");
                        break;
                    }
                    case NOTLIKE: {
                        p = builder.notLike(buildEx(root, name), "%" + value + "%");
                        break;
                    }
                    case BETWEEN: {
                        List<Comparable> cvalue = (List<Comparable>) value;
                        if (cvalue.size() != 2) {
                            continue;
                        }
                        p = builder.between(buildEx(root, name), cvalue.get(0), cvalue.get(1));
                        break;
                    }
                    case IN: {
                        Collection<Comparable> cvalue = (Collection<Comparable>) value;
                        CriteriaBuilder.In in = builder.in(buildEx(root, name));
                        in.value(cvalue);
                        p = in;
                        break;
                    }
                    case ISNULL: {
                        p = builder.isNull(buildEx(root, name));
                        break;
                    }
                    case ISNOTNULL: {
                        p = builder.isNotNull(buildEx(root, name));
                        break;
                    }
                    default: {
                        p = null;
                    }
                }
                if (p != null) {
                    if (p_ == null) {
                        p_ = p;
                    } else if (op == OP.AND) {
                        p_ = builder.and(p_, p);
                    } else if (op == OP.OR) {
                        p_ = builder.or(p_, p);
                    }
                }
            }

            return p_;
        };
    }

    public static <T> Expression<T> buildEx(Root<T> root, String key) {
        //级联搜索
        if (key.contains(".")) {
            String[] keys = key.split("\\.");
            return build(keys, root);
        } else {
            return root.get(key);
        }
    }

    public static <T> Expression<T> build(String[] keys, Root<T> root) {
        if (keys.length == 2) {
            return root.join(keys[0], JoinType.LEFT).get(keys[1]);
        } else {
            Join j = root.join(keys[0]);
            for (int i = 1; i < keys.length - 1; i++) {
                j = j.join(keys[i], JoinType.LEFT);
            }
            return j.get(keys[keys.length - 1]);
        }
    }

    public static class SpecElement {
        private Type oper;
        private String name;
        private Object value;

        public SpecElement(Type oper, String name, Object value) {
            this.oper = oper;
            this.name = name;
            this.value = value;
        }

        public Type getOper() {
            return oper;
        }

        public void setOper(Type oper) {
            this.oper = oper;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Object getValue() {
            return value;
        }

        public void setValue(Object value) {
            this.value = value;
        }

        @Override
        public String toString() {
            return "SpecElement{" +
                    "oper='" + oper + '\'' +
                    ", name='" + name + '\'' +
                    ", value=" + value +
                    '}';
        }

    }

    public enum Type {
        EQ, NEQ, GT, GE, LT, LE, LIKE, NOTLIKE, BETWEEN, IN, ISNULL, ISNOTNULL
    }

    public enum OP {
        AND, OR
    }

    /**
     * @author yaoy
     * @date 2020/7/20
     */
    public static SpecElement initSpecElement(Type oper, String name, String value) {
        if (value != null && value.isEmpty()) {
            value = null;
        }
        return new SpecElement(oper, name, value);
    }

    /**
     * @author yaoy
     * @date 2020/7/20
     * 0 - 正序  1 - 倒序  2 -默认序列
     */
    public static Page findPageParams(Specification s, String params, String order, int page, int size, JpaSpecificationExecutor repo, String defaultParam) {
        Sort.Direction direction = null;
        String sort = null;
        if ("0".equals(order)) {
            direction = Sort.Direction.ASC;
        } else {
            direction = Sort.Direction.DESC;
        }
        if (Strings.isNullOrEmpty(params) || "2".equals(order)) {
            sort = defaultParam;
        } else {
            sort = params;
        }
        Pageable pageable = PageRequest.of(page - 1, size, direction, sort);
        Page ret = repo.findAll(s, pageable);
        return ret;
    }
}
